/*
 * socketMng.c
 *
 *  Created on: 29/10/2011
 *      Author: utn_so
 */

#include "socketMng.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <semaphore.h>
#include "../../General/Serializable/serializable.h"

int32_t SocketMNG_Port;
char SocketMNG_IP[16];
int32_t SocketMNG_MaxConn;

typedef struct {
	SOCKET socket;
	int32_t onUse; //0 sin uso, 1 en uso
} __attribute__((packed)) conn_controller_t;

conn_controller_t * connController;

sem_t sem_conn;
pthread_mutex_t sem_connController = PTHREAD_MUTEX_INITIALIZER;

SOCKET SocketMNG_Handshake(SOCKET aSocket)
{
	Message aHandshakeMsg;
	Serializable_t aSerializable;
	int32_t Result;
	char DataReaded[32];
	if (aSocket == -1)
		return -1;

	aHandshakeMsg.type = msgHandShake;
	aHandshakeMsg.sectorRequest = 0;
	aHandshakeMsg.contentSize = 0;
	aHandshakeMsg.content = 0;

	Serialize_Message(&aSerializable, aHandshakeMsg);
	Result = send(aSocket, aSerializable.Data, aSerializable.Size, 0);
	Serializable_Free(&aSerializable);

	if (Result != -1)
		Result = recv(aSocket, DataReaded, HeaderSize, 0);
	if (Result > 0)
		return aSocket;
	else
		return -1;
}

SOCKET SocketMNG_initConnection()
{
	int32_t socketDescriptor;
	struct sockaddr_in pin;

	bzero(&pin, sizeof(pin));
	pin.sin_family = AF_INET;
	pin.sin_port = htons(SocketMNG_Port);
	pin.sin_addr.s_addr = inet_addr(SocketMNG_IP);

	socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(socketDescriptor, (void *)&pin, sizeof(pin)) != -1) {
		socketDescriptor = SocketMNG_Handshake(socketDescriptor);
		return socketDescriptor; //Si es --1 ocurrio un error
	}
	else
		return -1;
}

SOCKET SocketMNG_getConnection()
{
	sem_wait(&sem_conn);
	pthread_mutex_lock(&sem_connController);
	int32_t i = 0;
	SOCKET Result;

	while(i < SocketMNG_MaxConn && connController[i].onUse == 1)
		i++;

	if (i < SocketMNG_MaxConn) {//en todos los casos debe ser menor ya que el semaforo general controla la cantidad de sockets que entran
		connController[i].onUse = 1;
		Result = connController[i].socket;
	}
	else
		Result = -1;
	pthread_mutex_unlock(&sem_connController);
	return Result;
}

void SocketMNG_setMaxConnections(int32_t maxConn)
{
	int32_t i;
	SocketMNG_MaxConn = maxConn;
	connController = (conn_controller_t *)calloc(maxConn, sizeof(conn_controller_t));
	for(i = 0; i < maxConn; i++) {
		connController[i].socket = SocketMNG_initConnection();
		connController[i].onUse = 0;
		if(connController[i].socket == -1) {
			perror("Error al iniciar socket");
			exit(EXIT_FAILURE);
		}
	}
	sem_init(&sem_conn, 0, maxConn);
}

void SocketMNG_closeConnection(SOCKET aSocket)
{
	pthread_mutex_lock(&sem_connController);
	int32_t i = 0;

	while(i < SocketMNG_MaxConn && connController[i].socket != aSocket)
		i++;

	if (i < SocketMNG_MaxConn) //en todos los casos debe ser menor ya que el semaforo general controla la cantidad de sockets que entran
		connController[i].onUse = 0;

	pthread_mutex_unlock(&sem_connController);
	sem_post(&sem_conn);
}


int SocketMNG_readSector(int32_t aSector, char * Ptr)
{
	Message aMsg, rvMsg;
	Serializable_t aSerializable, rvSerializable;
	int32_t Result = -1;
	SOCKET aSocket;

	aMsg.type = msgRead;
	aMsg.sectorRequest = aSector;
	aMsg.contentSize = 0;
	aMsg.content = NULL;
	Serialize_Message(&aSerializable, aMsg);
	rvSerializable.Data = calloc(530, sizeof(char)); //espero recibir un msg desde el disco

	aSocket = SocketMNG_getConnection();
	if(aSocket != -1) {
		Result = send(aSocket, aSerializable.Data, aSerializable.Size, 0);
		if (Result != -1) {
			Result = recv(aSocket, rvSerializable.Data, HeaderSize, MSG_WAITALL);
			if (getSizeFromHeader(rvSerializable.Data) > 0) {
				Result += recv(aSocket, rvSerializable.Data + HeaderSize, getSizeFromHeader(rvSerializable.Data), MSG_WAITALL);
				rvSerializable.Size = getSizeFromHeader(rvSerializable.Data);
			}
		}
		if (Result != -1) {
			Deserialize_Message(&rvMsg, rvSerializable);
			memcpy(Ptr, rvMsg.content, rvMsg.contentSize);
			free(rvMsg.content);
		}
	}
	SocketMNG_closeConnection(aSocket);

	Serializable_Free(&aSerializable);
	Serializable_Free(&rvSerializable);
	return Result; //Si es -1 error
}


int SocketMNG_writeSector(int32_t aSector, char * Ptr, int32_t size)
{
	Message aMsg;
	Serializable_t aSerializable;
	int32_t Result;
	SOCKET aSocket;
	char response[128];

	aMsg.type = msgWrite;
	aMsg.sectorRequest = aSector;
	aMsg.contentSize = size;
	aMsg.content = Ptr;
	Serialize_Message(&aSerializable, aMsg);

	aSocket = SocketMNG_getConnection();
	Result = send(aSocket, aSerializable.Data, aSerializable.Size, 0) &&
			 recv(aSocket, response, HeaderSize, 0); //Falta manejar la respuesta, igualmente no aporta mucho
	SocketMNG_closeConnection(aSocket);

	Serializable_Free(&aSerializable);
	return Result; //Si es -1 error
}

void SocketMNG_setIP(const char * IP)
{
	strcpy(SocketMNG_IP, IP);
}

void SocketMNG_setPort(int32_t Port)
{
	SocketMNG_Port = Port;
}

int Handshake()
{
	Message aHandshakeMsg;
	Serializable_t aSerializable;
	int32_t Result;
	char DataReaded[32];
	SOCKET aSocket = SocketMNG_getConnection();
	if (aSocket == -1)
		return -1;

	aHandshakeMsg.type = msgHandShake;
	aHandshakeMsg.sectorRequest = 0;
	aHandshakeMsg.contentSize = 0;
	aHandshakeMsg.content = 0;

	Serialize_Message(&aSerializable, aHandshakeMsg);
	Result = send(aSocket, aSerializable.Data, aSerializable.Size, 0);
	Serializable_Free(&aSerializable);

	if (Result != -1)
		Result = recv(aSocket, DataReaded, 32, 0); //Me interesa que reciba algo, no el contenido
	SocketMNG_closeConnection(aSocket);
	return Result;
}

//void closeAllOpenConnections()
//{
//	int32_t i;
//	for(i = 0; i < SocketMNG_MaxConn; i++)
//		close(connController[i].socket);
//}
